/*
 * Lapack.cs
 *
 * LAPACK provider for AMD's ACML.
 *
 * Copyright (c) 2005, dnAnalytics. All rights reserved.
 */
using System;
using dnAnalytics.LinearAlgebra.Native;
using dnAnalytics.Math;

namespace dnAnalytics.LinearAlgebra.Native.Acml
{
    /// <summary>
    /// Summary description for Acml Lapack.
    /// </summary>
    internal sealed class Lapack : ILapack
    {
        private static readonly ILapack instance = new Lapack();

        private Lapack() { }

        #region Public Members

        public static ILapack Instance
        {
            get { return instance; }
        }

        public int Dgeqp3(int m, int n, double[] a, int lda, int[] jpvt, double[] tau)
        {
            double[] work = new double[System.Math.Max(1, 3 * n + 1)];
            return Dgeqp3(m, n, a, lda, jpvt, tau, work);
        }

        public int Dgeqp3(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgeqp3_(ref m, ref n, a, ref lda, jpvt, tau, work, ref len, ref info);
            return info;
        }

        public int Dgesvd(char jobu, char jobvt, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldvt)
        {
            double[] work = new double[System.Math.Max((3 * System.Math.Min(m, n) + System.Math.Max(m, n)), 5 * System.Math.Min(m, n))];
            return Dgesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, work);
        }

        public int Dgesvd(char jobu, char jobvt, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldvt, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgesvd_(ref jobu, ref jobvt, ref m, ref n, a, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref len, ref info, 1, 1);
            return info;
        }

        public int Dgetrf(int m, int n, double[] a, int lda, out int[] ipiv)
        {
            int info = 0;
            ipiv = new int[m];
            SafeNativeMethods.dgetrf_(ref m, ref n, a, ref lda, ipiv, ref info);
            return info;
        }

        public int Dgetri(int n, double[] a, int lda, int[] ipiv)
        {
            int bs = Ilaenv(1, "dgetri_", "", n, -1, -1, -1);
            double[] work = new double[n * bs];
            return Dgetri(n, a, lda, ipiv, work);
        }

        public int Dgetri(int n, double[] a, int lda, int[] ipiv, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgetri_(ref n, a, ref lda, ipiv, work, ref len, ref info);
            return info;
        }

        public int Dgetrs(char trans, int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.dgetrs_(ref trans, ref n, ref nrhs, a, ref lda, ipiv, b, ref ldb, ref info, 1);
            return info;
        }

        public int Dorgqr(int m, int n, int k, double[] a, int lda, double[] tau)
        {
            int bs = Ilaenv(1, "dorgqr_", "", m, n, k, -1);
            double[] work = new double[n * bs];
            return Dorgqr(m, n, k, a, lda, tau, work);
        }

        public int Dorgqr(int m, int n, int k, double[] a, int lda, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dorgqr_(ref m, ref n, ref k, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Dormqr(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc)
        {
            string opts = side.ToString() + trans.ToString();
            int bs = Ilaenv(1, "dormqr_", opts, m, n, k, -1);
            double[] work = null;
            if (side == 'L' || side == 'l')
            {
                work = new double[n * bs];
            }
            else
            {
                work = new double[m * bs];
            }
            return Dormqr(side, trans, m, n, k, a, lda, tau, c, ldc, work);
        }

        public int Dormqr(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dormqr_(ref side, ref trans, ref m, ref n, ref k, a, ref lda, tau, c, ref ldc, work, ref len, ref info, 1, 1);
            return info;
        }

        public int Dpotrf(char uplo, int n, double[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.dpotrf_(ref uplo, ref n, a, ref lda, ref info, 1);
            return info;
        }

        public int Dpotri(char uplo, int n, double[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.dpotri_(ref uplo, ref n, a, ref lda, ref info, 1);
            return info;
        }

        public int Dpotrs(char uplo, int n, int nrhs, double[] a, int lda, double[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.dpotrs_(ref uplo, ref n, ref nrhs, a, ref lda, b, ref ldb, ref info, 1);
            return info;
        }

        public int Zgeqp3(int m, int n, Complex[] a, int lda, int[] jpvt, Complex[] tau)
        {
            Complex[] work = new Complex[System.Math.Max(1, n + 1)];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgeqp3(m, n, a, lda, jpvt, tau, work, rwork);
        }

        public int Zgeqp3(int m, int n, Complex[] a, int lda, int[] jpvt, Complex[] tau, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zgeqp3_(ref m, ref n, a, ref lda, jpvt, tau, work, ref len, rwork, ref info);
            return info;
        }

        public int Zgetrf(int m, int n, Complex[] a, int lda, out int[] ipiv)
        {
            int info = 0;
            ipiv = new int[m];
            SafeNativeMethods.zgetrf_(ref m, ref n, a, ref lda, ipiv, ref info);
            return info;
        }

        public int Zgetri(int n, Complex[] a, int lda, int[] ipiv)
        {
            int bs = Ilaenv(1, "zgetri_", "", n, -1, -1, -1);
            Complex[] work = new Complex[n * bs];
            return Zgetri(n, a, lda, ipiv, work);
        }

        public int Zgetri(int n, Complex[] a, int lda, int[] ipiv, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zgetri_(ref n, a, ref lda, ipiv, work, ref len, ref info);
            return info;
        }

        public int Zgetrs(char trans, int n, int nrhs, Complex[] a, int lda, int[] ipiv, Complex[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.zgetrs_(ref trans, ref n, ref nrhs, a, ref lda, ipiv, b, ref ldb, ref info, 1);
            return info;
        }

        public int Zpotrf(char uplo, int n, Complex[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.zpotrf_(ref uplo, ref n, a, ref lda, ref info, 1);
            return info;
        }

        public int Zpotri(char uplo, int n, Complex[] a, int lda)
        {
            int info = 0;
            SafeNativeMethods.zpotri_(ref uplo, ref n, a, ref lda, ref info, 1);
            return info;
        }

        public int Zpotrs(char uplo, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb)
        {
            int info = 0;
            SafeNativeMethods.zpotrs_(ref uplo, ref n, ref nrhs, a, ref lda, b, ref ldb, ref info, 1);
            return info;
        }

        public int Zungqr(int m, int n, int k, Complex[] a, int lda, Complex[] tau)
        {
            int bs = Ilaenv(1, "zungqr_", "", m, n, k, -1);
            Complex[] work = new Complex[n * bs];
            return Zungqr(m, n, k, a, lda, tau, work);
        }

        public int Zungqr(int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zungqr_(ref m, ref n, ref k, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Zunmqr(char side, char trans, int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] c, int ldc)
        {
            string opts = side.ToString() + trans.ToString();
            int bs = Ilaenv(1, "zunmqr_", opts, m, n, k, -1);
            Complex[] work = null;
            if (side == 'L' || side == 'l')
            {
                work = new Complex[n * bs];
            }
            else
            {
                work = new Complex[m * bs];
            }
            return Zunmqr(side, trans, m, n, k, a, lda, tau, c, ldc, work);
        }

        public int Zunmqr(char side, char trans, int m, int n, int k, Complex[] a, int lda, Complex[] tau, Complex[] c, int ldc, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zunmqr_(ref side, ref trans, ref m, ref n, ref k, a, ref lda, tau, c, ref ldc, work, ref len, ref info, 1, 1);
            return info;
        }

        public int Zgesvd(char jobu, char jobvt, int m, int n, Complex[] a, int lda, double[] s, Complex[] u, int ldu, Complex[] vt, int ldvt)
        {
            Complex[] work = new Complex[2 * System.Math.Min(m, n) + System.Math.Max(m, n)];
            double[] rwork = new double[System.Math.Max(1, 5 * System.Math.Min(m, n))];
            return Zgesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, work, rwork);
        }

        public int Zgesvd(char jobu, char jobvt, int m, int n, Complex[] a, int lda, double[] s, Complex[] u, int ldu, Complex[] vt, int ldvt, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;

            SafeNativeMethods.zgesvd_(ref jobu, ref jobvt, ref m, ref n, a, ref lda, s, u, ref ldu, vt, ref ldvt, work, ref len, rwork, ref info, 1, 1);
            return info;
        }

        public int Dgels(char trans, int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb)
        {
            int bs = Ilaenv(1, "dgels_", trans.ToString(), m, n, nrhs, -1);
            double[] work = new double[System.Math.Min(m, n) + System.Math.Max(m, System.Math.Max(n, nrhs)) * bs];
            return Dgels(trans, m, n, nrhs, a, lda, b, ldb, work);
        }

        public int Dgels(char trans, int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgels_(ref trans, ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, work, ref len, ref info, 1);
            return info;
        }

        public int Zgels(char trans, int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb)
        {
            int bs = Ilaenv(1, "zgels_", trans.ToString(), m, n, nrhs, -1);
            Complex[] work = new Complex[System.Math.Min(m, n) + System.Math.Max(m, System.Math.Max(n, nrhs)) * bs];
            return Zgels(trans, m, n, nrhs, a, lda, b, ldb, work);
        }

        public int Zgels(char trans, int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zgels_(ref trans, ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, work, ref len, ref info, 1);
            return info;
        }

        public int Dgelss(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, out double[] s, ref int rank)
        {
            double[] work = new double[3 * System.Math.Min(m, n) + System.Math.Max(2 * System.Math.Min(m, n), System.Math.Max(System.Math.Max(m, n), nrhs))];
            double rcond = -1;
            return Dgelss(m, n, nrhs, a, lda, b, ldb, out s, rcond, ref rank, work);
        }

        public int Dgelss(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, out double[] s, double rcond, ref int rank, double[] work)
        {
            int info = 0;
            int len = work.Length;
            s = new double[System.Math.Min(m, n)];
            SafeNativeMethods.dgelss_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, s, ref rcond, ref rank, work, ref len, ref info);
            return info;
        }

        public int Zgelss(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, out double[] s, ref int rank)
        {
            Complex[] work = new Complex[3 * System.Math.Min(m, n) + System.Math.Max(2 * System.Math.Min(m, n), System.Math.Max(System.Math.Max(m, n), nrhs))];
            double[] rwork = new double[System.Math.Max(1, 5 * System.Math.Min(m, n))];
            double rcond = -1;
            return Zgelss(m, n, nrhs, a, lda, b, ldb, out s, rcond, ref rank, work, rwork);
        }

        public int Zgelss(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, out double[] s, double rcond, ref int rank, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            s = new double[System.Math.Min(m, n)];
            SafeNativeMethods.zgelss_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, s, ref rcond, ref rank, work, ref len, rwork, ref info);
            return info;
        }

        public int Dgelsy(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, int[] jpvt, double rcond, ref int rank)
        {
            int nb = Ilaenv(1, "dgelsy_", "", m, n, nrhs, -1);
            int mn = System.Math.Min(m, n);
            int len = System.Math.Max((mn + 2) * n + nb * (n + 1), 2 * mn + nb * nrhs);
            double[] work = new double[len];
            return Dgelsy(m, n, nrhs, a, lda, b, ldb, jpvt, rcond, ref rank, work);
        }

        public int Dgelsy(int m, int n, int nrhs, double[] a, int lda, double[] b, int ldb, int[] jpvt, double rcond, ref int rank, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgelsy_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, jpvt, ref rcond, ref rank, work, ref len, ref info);
            return info;
        }

        public int Zgelsy(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, int[] jpvt, double rcond, ref int rank)
        {
            int nb = Ilaenv(1, "zgelsy_", "", m, n, nrhs, -1);
            int mn = System.Math.Min(m, n);
            int len = mn + System.Math.Max(System.Math.Max(System.Math.Max(2 * mn, nb * (n + 1)), mn + mn * nb), mn + nb * nrhs);
            Complex[] work = new Complex[len];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgelsy(m, n, nrhs, a, lda, b, ldb, jpvt, rcond, ref rank, work, rwork);
        }

        public int Zgelsy(int m, int n, int nrhs, Complex[] a, int lda, Complex[] b, int ldb, int[] jpvt, double rcond, ref int rank, Complex[] work, double[] rwork)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zgelsy_(ref m, ref n, ref nrhs, a, ref lda, b, ref ldb, jpvt, ref rcond, ref rank, work, ref len, rwork, ref info);
            return info;
        }

        public double Dlange(char norm, int m, int n, double[] a, int lda)
        {
            double[] work = new double[m];
            return Dlange(norm, m, n, a, lda, work);
        }

        public double Dlange(char norm, int m, int n, double[] a, int lda, double[] work)
        {
            return SafeNativeMethods.dlange_(ref norm, ref m, ref n, a, ref lda, work, 1);
        }

        public double Zlange(char norm, int m, int n, Complex[] a, int lda)
        {
            double[] work = new double[m];
            return Zlange(norm, m, n, a, lda, work);
        }

        public double Zlange(char norm, int m, int n, Complex[] a, int lda, double[] work)
        {
            return SafeNativeMethods.zlange_(ref norm, ref m, ref n, a, ref lda, work, 1);
        }

        public int Dgecon(char norm, int n, double[] a, int lda, double anorm, ref double rcond)
        {
            double[] work = new double[System.Math.Max(1, 4 * n)];
            int[] iwork = new int[System.Math.Max(1, n)];
            return Dgecon(norm, n, a, lda, anorm, ref rcond, work, iwork);
        }

        public int Dgecon(char norm, int n, double[] a, int lda, double anorm, ref double rcond, double[] work, int[] iwork)
        {
            int info = 0;
            SafeNativeMethods.dgecon_(ref norm, ref n, a, ref lda, ref anorm, ref rcond, work, iwork, ref info, 1);
            return info;
        }

        public int Zgecon(char norm, int n, Complex[] a, int lda, double anorm, ref double rcond)
        {
            Complex[] work = new Complex[System.Math.Max(1, 4 * n)];
            double[] rwork = new double[System.Math.Max(1, 2 * n)];
            return Zgecon(norm, n, a, lda, anorm, ref rcond, work, rwork);
        }

        public int Zgecon(char norm, int n, Complex[] a, int lda, double anorm, ref double rcond, Complex[] work, double[] rwork)
        {
            int info = 0;
            SafeNativeMethods.zgecon_(ref norm, ref n, a, ref lda, ref anorm, ref rcond, work, rwork, ref info, 1);
            return info;
        }

        public int Ilaenv(int ispec, string name, string opts, int n1, int n2, int n3, int n4)
        {
            return SafeNativeMethods.ilaenv_(ref ispec, name, opts, ref n1, ref n2, ref n3, ref n4, name.Length, opts.Length);
        }

        public int Dgeqrf(int m, int n, double[] a, int lda, double[] tau)
        {
            int nb = Ilaenv(1, "dgeqrf_", "", m, n, -1, -1);
            double[] work = new double[n * nb];
            return Dgeqrf(m, n, a, lda, tau, work);
        }

        public int Dgeqrf(int m, int n, double[] a, int lda, double[] tau, double[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.dgeqrf_(ref m, ref n, a, ref lda, tau, work, ref len, ref info);
            return info;
        }

        public int Zgeqrf(int m, int n, Complex[] a, int lda, Complex[] tau)
        {
            int nb = Ilaenv(1, "zgeqrf_", "", m, n, -1, -1);
            Complex[] work = new Complex[n * nb];
            return Zgeqrf(m, n, a, lda, tau, work);
        }

        public int Zgeqrf(int m, int n, Complex[] a, int lda, Complex[] tau, Complex[] work)
        {
            int info = 0;
            int len = work.Length;
            SafeNativeMethods.zgeqrf_(ref m, ref n, a, ref lda, tau, work, ref len, ref info);
            return info;
        }


        #endregion Public Members
    }
}